Child Health and Economic Prosperity: A Global Analysis of ORT Coverage

Author

Vishal - A00046486 | DCU Business School |

1 A Story of Care and Resilience

In a world often focused on groundbreaking medical advancements, the simple yet life-saving power of a mixture of salt, sugar, and clean water is frequently overlooked. Diarrhoea remains one of the leading causes of death among children under five, particularly in low-income regions. The tragedy lies not just in the illness itself, but in the fact that the treatment — Oral Rehydration Therapy (ORT) — is well-known, highly effective, and affordable. Yet, millions of children still go without it due to barriers such as poverty, misinformation, and weak healthcare systems. This report sheds light on these hidden struggles, using global data to represent the lives of children, families, and healthcare workers. Through these visuals, you’ll uncover trends of progress, areas of disparity, and the significant impact of community health initiatives. Behind every data point is a child whose life was saved — or could have been — by this simple solution. This isn’t just a data analysis; it’s a story of resilience, equity, and the collective ability to save lives — one sip at a time.

Show/Hide Code
import polars as pl
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import geopandas as gpd
import statsmodels.api as sm
from plotly.subplots import make_subplots

# note these files are already cleaned and the null values are removed from them 

try:
    diarrhoea_df = pl.read_csv("cleaned_data_1_no_null.csv")
    metadata_df = pl.read_csv("cleaned_data_0_no_null.csv")
except FileNotFoundError as e:
    print(f"Error loading data files: {e}")
    print("Please ensure both CSV files are in the working directory")
    exit()


sample_df = diarrhoea_df.to_pandas()

df = diarrhoea_df.join(
    metadata_df,
    on=["country"],
    how="inner"
).filter(
    (pl.col("sex") == "Total") &
    (pl.col("time_period") == pl.col("year"))
)
pdf = df.to_pandas()
pdf = pdf.sort_values("time_period")

2 Key Indicators of Global Diarrhoea Treatment Efforts

Show/Hide Code
import plotly.graph_objects as go

# just taking there average here 
avg_obs = sample_df['obs_value'].mean()

fig1 = go.Figure(go.Indicator(
    mode="number",
    value=avg_obs,
    number={'valueformat': '.1f', 'font': {'size': 50, 'color': '#81c784'}},
    title={"text": "Average ORT Coverage (%)", 'font': {'size': 20}}
))
fig1.update_layout(height=300, margin=dict(t=40, b=10, l=10, r=10))
fig1.show()
Show/Hide Code
#this to how many we have 
record_count = sample_df.shape[0]

fig2 = go.Figure(go.Indicator(
    mode="number",
    value=record_count,
    number={'font': {'size': 50, 'color': '#81c784'}},
    title={"text": "Total Data Points", 'font': {'size': 20}}
))
fig2.update_layout(height=300, margin=dict(t=40, b=10, l=10, r=10))
fig2.show()
Show/Hide Code
distinct_countries = sample_df['country'].nunique()

fig3 = go.Figure(go.Indicator(
    mode="number",
    value=distinct_countries,
    number={'font': {'size': 50, 'color': '#81c784'}},
    title={"text": "Countries Analyzed", 'font': {'size': 20}}
))
fig3.update_layout(height=300, margin=dict(t=40, b=10, l=10, r=10))
fig3.show()

3 Patterns, Progress, and Pain Points

3.2 Lifesaving Sips: The Unequal Global Reach of Oral Rehydration Therapy:

Show/Hide Code
import pycountry_convert as pc

# Function to get continent from country code
def get_continent(country_code):
    try:
        country_alpha2 = pc.country_alpha3_to_country_alpha2(country_code)
        continent_code = pc.country_alpha2_to_continent_code(country_alpha2)
        continent_name = pc.convert_continent_code_to_continent_name(continent_code)
        return continent_name
    except:
        return None



latest_years = sample_df.groupby('alpha_3_code')['time_period'].max().reset_index()

latest_data = pd.merge(sample_df, latest_years, on=['alpha_3_code', 'time_period'])

latest_data = latest_data[latest_data['sex'] == 'Total']


latest_data['region'] = latest_data['alpha_3_code'].apply(get_continent)

latest_data = latest_data.dropna(subset=['region'])

top_countries = latest_data.sort_values(['region', 'obs_value'], ascending=[True, False]) \
                          .groupby('region') \
                          .head(3)

region_avg = latest_data.groupby('region')['obs_value'].mean().reset_index()
region_avg.columns = ['region', 'avg_ort']

sunburst_data = []


for _, row in region_avg.iterrows():
    sunburst_data.append({
        'id': row['region'],
        'parent': '',
        'label': row['region'],
        'value': row['avg_ort'],
        'color': row['avg_ort']
    })

for _, row in top_countries.iterrows():
    sunburst_data.append({
        'id': f"{row['region']}-{row['country']}",
        'parent': row['region'],
        'label': row['country'],
        'value': row['obs_value'],
        'color': row['obs_value']
    })

sunburst_df = pd.DataFrame(sunburst_data)
# Create custom color scale from #ecf8e8 to #004e1f
custom_color_scale = [
    [0.0, '#ecf8e8'],
    [0.2, '#c7e9c0'],
    [0.4, '#a1d99b'],
    [0.6, '#74c476'],
    [0.8, '#31a354'],
    [1.0, '#004e1f']
]



fig = px.sunburst(
    sunburst_df,
    names='label',
    parents='parent',
    values='value',
    color='color',
    color_continuous_scale=custom_color_scale, 
    range_color=[0, 100],  
    title='',
    width=800,
    height=800
)


fig.update_layout(
    margin=dict(t=50, l=0, r=0, b=0),
    coloraxis_colorbar=dict(
        title='ORT %',
        ticksuffix='%'
    )
)


fig.show()
Green Shades of Hope—and Neglect

Diarrhea remains one of the leading killers of children under five, yet its simplest cure—oral rehydration therapy (ORT)—is tragically uneven in its reach. This sunburst chart reveals a stark divide: while some regions have embraced ORT, saving countless young lives, others still struggle to deliver this basic, life-saving intervention.

The deeper greens show where ORT is widely available—where a packet of salts and clean water can turn despair into hope. The lighter shades expose gaps in care, where preventable deaths still occur. Each ring tells a story of progress and neglect, of innovation and inertia.

This isn’t just data—it’s a map of survival. The question isn’t whether ORT works, but whether it reaches every child who needs it. The answer could mean the difference between life and loss for millions.

3.3 Bar Chart: ORT Treatement by Gender

Show/Hide Code
import plotly.graph_objects as go


countries = sample_df["country"].unique()
fig_bars = go.Figure()

# my color map
color_map = {
    "Male": "rgba(22,128,60, 0.7)",
    "Female": "rgba(179,225,173,255)",
    "Total": "rgba(0,103,42,255)"
}

#
for country in countries:
    df_filtered = sample_df[sample_df["country"] == country]
    df_latest = df_filtered[df_filtered["time_period"] == df_filtered["time_period"].max()]
    
    fig_bars.add_trace(go.Bar(
        x=df_latest["sex"],
        y=df_latest["obs_value"],
        name=country,
        visible=(country == countries[0]),
        marker=dict(color=[color_map.get(sex, "rgba(44, 160, 44, 0.7)") for sex in df_latest["sex"]]),
        width=0.3  
    ))


buttons = [
    dict(
        label=country,
        method="update",
        args=[
            {"visible": [c == country for c in countries]},
            {"title.text": f"{country}"}
        ]
    )
    for country in countries
]

fig_bars.update_layout(
    updatemenus=[
        dict(
            buttons=buttons,
            direction="down",
            showactive=False,
            x=1.05,
            xanchor="left",
            y=1.0,
            yanchor="top",
            pad={"r": 4, "t": 4},
            font=dict(size=12)
        )
    ],
    title={"text": f" {countries[0]}", "x": 0.5},
    xaxis_title="Gender",
    yaxis_title="% Receiving ORT",
    template="plotly_white",
    bargap=0.2,
    barmode='group'
)

fig_bars.show()
Equal Treatment, Promising Progress

Global data shows near-identical ORT treatment rates for boys and girls with diarrhea, with girls slightly ahead in recent years—a significant achievement in health equity. This parity reflects successful interventions ensuring equal access to this life-saving care. While treatment rates are balanced, subtle differences may persist in prevention and follow-up. The equal bars represent real progress, proving that when simple, effective solutions are prioritized, gender gaps in basic healthcare can be closed. However, continued focus is needed to maintain and expand this equity across all aspects of child health

3.4 Trend Over Time for Selected Country

Show/Hide Code
import plotly.express as px
import plotly.graph_objects as go

df_total = sample_df[sample_df["sex"] == "Total"]
countries = df_total["country"].unique()

fig = go.Figure()


pattern_shapes = ["x", "/", "\\", ".", "+"]
green_transparent = "rgba(0, 128, 0, 0.3)"  

for i, country in enumerate(countries):
    df_country = df_total[df_total["country"] == country]
    area = px.area(
        df_country,
        x="time_period",
        y="obs_value",
        title=f"ORT Trend Over Time: {country}",
        pattern_shape_sequence=[pattern_shapes[i % len(pattern_shapes)]],
    )
    
    for trace in area.data:
        trace.update(
            line=dict(color="darkgreen", width=1.5),  
            fillcolor=green_transparent,  
            visible=(i == 0)
        )
        fig.add_trace(trace)


buttons = [
    dict(label=country,
         method="update",
         args=[{"visible": [j // 1 == i for j in range(len(countries))]},
               {"title": f"ORT Trend Over Time: {country}"}])
    for i, country in enumerate(countries)
]

fig.update_layout(
    updatemenus=[dict(active=0, buttons=buttons)],
    xaxis_title="Year",
    yaxis_title="ORT Coverage (%)",
    template="plotly_white"
)

fig.show()
The Journey of a Nation

Every country has its own story, especially when it comes to the health of its youngest citizens. The “Trend Over Time for Selected Country” section lets you explore each nation’s journey in combating diarrhoea among children under 5. As you select a country, you’ll follow its progress in providing Oral Rehydration Therapy (ORT), with each rise and fall on the graph representing milestones—successes, challenges, and health campaigns. Behind these data points are the lives of children profoundly impacted by ORT access.

This is more than just trend analysis; it’s a testament to each nation’s efforts toward better child health. It calls us to celebrate victories, learn from setbacks, and work towards ensuring every child has access to life-saving care. Let these trends inspire action and remind us that every child deserves a healthy life.

3.5 Scatter Plot: GDP vs ORT Coverage

Show/Hide Code
fig_gdp_scatter = px.scatter(
    pdf,
    x="GDP per capita (constant 2015 US$)",
    y="obs_value",
    color="country",
    title="GDP per Capita vs ORT Coverage",
    labels={
        "GDP per capita (constant 2015 US$)": "GDP per capita (2015 US$)",
        "obs_value": "% Receiving ORT"
    }
)

X = pdf[["GDP per capita (constant 2015 US$)"]]
X = sm.add_constant(X)
y = pdf["obs_value"]
model = sm.OLS(y, X).fit()
pdf["regression"] = model.predict(X)

fig_gdp_scatter.add_traces(go.Scatter(
    x=pdf["GDP per capita (constant 2015 US$)"],
    y=pdf["regression"],
    mode="lines",
    name="Regression Line",
    line=dict(color="black", dash="dot")
))

fig_gdp_scatter.update_layout(template="plotly_white")
fig_gdp_scatter.show()
The Intersection of Wealth and Health

The scatter plot explores the relationship between a country’s wealth, measured by GDP, and its ORT coverage for children under 5. Each dot represents a country, showing its economic strength and commitment to child health. While wealthier nations generally have higher ORT coverage, there are exceptions, with some economically challenged countries achieving impressive results. This plot highlights that GDP is not the sole determinant of healthcare success—policies, priorities, and equity also play key roles. Let it inspire action and remind us that every child, regardless of a nation’s wealth, deserves access to life-saving healthcare.

4 Understanding Country Readiness

Show/Hide Code
from IPython.display import display, HTML
latest_year = metadata_df["year"].max()


latest_data = metadata_df.filter(pl.col("year") == latest_year)

columns_needed = [
    "country",
    "Population, total",
    "GDP per capita (constant 2015 US$)",
    "Life expectancy at birth, total (years)",
    "GDP growth (annual %)"
]

latest_data = latest_data.select(columns_needed)


html_code = """
<div style="display: flex; overflow-x: auto; padding: 20px; gap: 1.5rem; scroll-snap-type: x mandatory;">
"""

for row in latest_data.iter_rows(named=True):
    country = row["country"]
    population = f"{row['Population, total']:,}" if row['Population, total'] else "N/A"
    gdp_per_capita = f"${row['GDP per capita (constant 2015 US$)']:,.2f}" if row['GDP per capita (constant 2015 US$)'] else "N/A"
    life_expectancy = f"{row['Life expectancy at birth, total (years)']:.1f} years" if row['Life expectancy at birth, total (years)'] else "N/A"
    gdp_growth = f"{row['GDP growth (annual %)']:.2f}%" if row['GDP growth (annual %)'] else "N/A"

    html_code += f"""
        <div style="
            flex: 0 0 auto;
            width: 300px;
            height: 260px;
            background: rgba(227, 227, 227, 0.08);
            border-radius: 10px;
            box-shadow: 0 7px 15px rgba(0,0,0,0.2);
            display: flex;
            flex-direction: column;
            align-items: left;
            justify-content: center;
            font-family: 'Poppins', 'Segoe UI', 'Roboto', sans-serif;
            scroll-snap-align: center;
            padding: 20px;
            text-align: left;
            transition: transform 0.3s ease;
            backdrop-filter: blur(5px);
            overflow: hidden;
            word-wrap: break-word;
            white-space: normal;
            overflow-wrap: break-word;
        "
        onmouseover="this.style.transform='scale(1.05)';" 
        onmouseout="this.style.transform='scale(1)';"
        >
        <div style="flex: 1; display: flex; align-items: left; justify-content: left;">
        <h2 style="font-size: 24px; font-weight: 600;">{country}</h2>
    </div>

    <!-- RIGHT SIDE: KPIs -->
    <div style="flex: 2; display: flex; flex-direction: column; justify-content: center;">
        <p><strong>GDP: </strong>{gdp_per_capita}</p>
        <p><strong>Population: </strong>{population}</p>
        <p><strong>Life Expectancy: </strong>{life_expectancy}</p>
        <p><strong>Growth Rate: </strong>{gdp_growth}</p>
    </div>
    </div>
    """

html_code += "</div>"


display(HTML(html_code))

Australia

GDP: $56,739.03

Population: 23,815,995.0

Life Expectancy: 82.4 years

Growth Rate: 2.19%

Austria

GDP: $43,915.23

Population: 8,642,699.0

Life Expectancy: 81.2 years

Growth Rate: 1.30%

Belgium

GDP: $40,893.80

Population: 11,274,196.0

Life Expectancy: 81.0 years

Growth Rate: 1.47%

Canada

GDP: $43,594.19

Population: 35,704,498.0

Life Expectancy: 81.8 years

Growth Rate: 0.65%

Chile

GDP: $13,433.92

Population: 18,047,625.0

Life Expectancy: 79.7 years

Growth Rate: 2.15%

Czech Republic

GDP: $17,931.60

Population: 10,546,059.0

Life Expectancy: 78.6 years

Growth Rate: 4.96%

Denmark

GDP: $53,094.01

Population: 5,683,483.0

Life Expectancy: 80.7 years

Growth Rate: 2.10%

Estonia

GDP: $17,722.16

Population: 1,315,407.0

Life Expectancy: 77.6 years

Growth Rate: 1.84%

Finland

GDP: $42,560.35

Population: 5,479,531.0

Life Expectancy: 81.5 years

Growth Rate: 0.47%

France

GDP: $36,702.43

Population: 66,548,272.0

Life Expectancy: 82.3 years

Growth Rate: 1.07%

Germany

GDP: $41,911.01

Population: 81,686,611.0

Life Expectancy: 80.6 years

Growth Rate: 1.65%

Greece

GDP: $17,980.73

Population: 10,820,883.0

Life Expectancy: 81.0 years

Growth Rate: -0.23%

Hungary

GDP: $12,717.04

Population: 9,843,028.0

Life Expectancy: 75.6 years

Growth Rate: 3.71%

Ireland

GDP: $64,311.82

Population: 4,701,957.0

Life Expectancy: 81.5 years

Growth Rate: 24.62%

Israel

GDP: $36,123.99

Population: 8,380,100.0

Life Expectancy: 82.1 years

Growth Rate: 2.31%

Italy

GDP: $30,387.13

Population: 60,730,582.0

Life Expectancy: 82.5 years

Growth Rate: 0.89%

Japan

GDP: $34,960.64

Population: 127,141,000.0

Life Expectancy: 83.8 years

Growth Rate: 1.56%

South Korea

GDP: $28,737.44

Population: 51,014,947.0

Life Expectancy: 82.0 years

Growth Rate: 2.81%

Luxembourg

GDP: $105,462.01

Population: 569,604.0

Life Expectancy: 82.3 years

Growth Rate: 2.27%

Mexico

GDP: $10,021.24

Population: 121,072,306.0

Life Expectancy: 74.7 years

Growth Rate: 2.70%

Netherlands

GDP: $45,793.81

Population: 16,939,923.0

Life Expectancy: 81.5 years

Growth Rate: 2.12%

New Zealand

GDP: $38,639.35

Population: 4,609,400.0

Life Expectancy: 81.6 years

Growth Rate: 3.73%

Norway

GDP: $74,809.97

Population: 5,188,607.0

Life Expectancy: 82.3 years

Growth Rate: 1.86%

Poland

GDP: $12,637.52

Population: 37,986,412.0

Life Expectancy: 77.5 years

Growth Rate: 4.43%

Portugal

GDP: $19,215.78

Population: 10,358,076.0

Life Expectancy: 81.1 years

Growth Rate: 1.59%

Slovakia

GDP: $16,442.08

Population: 5,423,801.0

Life Expectancy: 76.6 years

Growth Rate: 5.18%

Slovenia

GDP: $20,697.27

Population: 2,063,531.0

Life Expectancy: 80.8 years

Growth Rate: 2.39%

Spain

GDP: $25,969.84

Population: 46,444,832.0

Life Expectancy: 82.8 years

Growth Rate: 4.06%

Sweden

GDP: $51,197.95

Population: 9,799,186.0

Life Expectancy: 82.2 years

Growth Rate: 4.41%

Switzerland

GDP: $83,806.45

Population: 8,282,396.0

Life Expectancy: 82.9 years

Growth Rate: 1.64%

Turkey

GDP: $11,050.00

Population: 78,218,479.0

Life Expectancy: 76.6 years

Growth Rate: 6.08%

United Kingdom

GDP: $44,964.39

Population: 65,116,219.0

Life Expectancy: 81.0 years

Growth Rate: 2.22%

United States

GDP: $57,040.21

Population: 320,738,994.0

Life Expectancy: 78.7 years

Growth Rate: 2.95%


This section gives a broader view of how countries are positioned to tackle childhood diarrhoea using simple solutions like Oral Rehydration Therapy (ORT). By looking at GDP per capita, life expectancy, population size, and growth rate, we can gauge the resources, health systems, and challenges each nation faces.

It highlights which countries are better prepared and where more support or stronger policies are needed. Behind each metric is a reminder: fighting diarrhoea isn’t just about medicine — it’s about building strong systems and ensuring every child has a fair chance at a healthy life.

4.1 Range of ORT Coverage Over Time

Show/Hide Code
import plotly.graph_objects as go


range_df = sample_df.groupby("time_period")["obs_value"].agg(['min', 'max']).reset_index()
range_df["trend"] = (range_df["min"] + range_df["max"]) / 2


gradient_color = "rgba(0, 128, 0, 0.3)" 


fig_range = go.Figure([
    
    go.Scatter(x=range_df["time_period"], y=range_df["max"],
               mode="lines", name="Max", line=dict(width=0)),

    
    go.Scatter(x=range_df["time_period"], y=range_df["min"],
               fill='tonexty', fillcolor=gradient_color,
               mode="lines", name="Min", line=dict(width=0)),

    
    go.Scatter(x=range_df["time_period"], y=range_df["trend"],
               mode="lines", name="Trend", line=dict(color="green", width=2))
])

fig_range.update_layout(
    title="Range of ORT Coverage Over Time",
    xaxis_title="Year",
    yaxis_title="ORT Coverage (%)",
    template="plotly_white"
)

fig_range.show()
The Spectrum of Progress

graph showcases the diverse journeys nations have taken in expanding access to Oral Rehydration Therapy (ORT) for children under 5. Each line represents a country, with its length showing the range of ORT coverage over time—from the lowest to the highest levels recorded. The graph reveals both progress and disparity, with some countries showing significant improvements while others still face challenges.

This graph is more than just data; it’s a story of hope and a reminder of the work still to be done. It highlights the importance of persistence, healthcare access, and the difference we can make in the lives of children. Every child deserves a healthy life, and it’s up to us to ensure they get it.

5 Conclusion

The visual narratives we’ve explored—world map, country trends, GDP vs ORT coverage, and ORT range over time—converge on one powerful message: the fight against diarrhoea in children under 5 is far from over. These charts are not just data; they reflect our progress, highlight disparities, and call us to action.

While wealth influences healthcare, it’s not the only factor. Many countries have made significant strides in ORT coverage regardless of their economic status. We’ve seen the persistence of gender disparities and tracked the unique journeys of nations striving to improve access to ORT.

At the heart of these stories are the children whose survival depends on a simple, affordable treatment. As we move forward, let these narratives inspire action, reminding us of the importance of equitable healthcare and the impact we can have on children’s lives.

Every child deserves a healthy start, and it’s up to us to make that a reality. Let’s continue the fight against diarrhoea and ensure every child has access to life-saving ORT, for a healthier future for all.